home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1998 August: Tool Chest / Dev.CD Aug 98 TC.toast / Sample Code / Snippets / Networking / ADSP Chat / Main.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-09-06  |  35.8 KB  |  1,317 lines  |  [TEXT/MPS ]

  1. /*****************************************************************
  2.  
  3.     Program:    < ADSP Chat >
  4.     File:        < Main.c >
  5.     
  6.     Written by  Pete Helm, Scott Kuechle
  7.     of <Apple Macintosh Developer Technical Support>
  8.     
  9.     modified by Scott Kuechle
  10.     10/92 SRK Converted from Pascal to C
  11.     8/94 SRK Modified to use a queue of parameter
  12.              blocks.
  13.  
  14.     Copyright © 1992, 1994 Apple Computer, Inc.
  15.     All rights reserved.
  16.     
  17. *****************************************************************/
  18.  
  19.  
  20. /*****************************************************************/
  21. /*  I N C L U D E S
  22. /*****************************************************************/
  23.  
  24. #include    "ADSP Chat.h"
  25.  
  26. /*****************************************************************/
  27. /*  G L O B A L   V A R I A B L E   D E C L A R A T I O N S
  28. /*****************************************************************/
  29.  
  30. DialogPtr myDialog;
  31.  
  32. Boolean gHasWaitNextEvent,gInBackground,gGrowRect,gStopped,gStopRect,gGoRect,gHasSystem7;
  33. Boolean gMoofEntityFilter;
  34.  
  35. MenuHandle ZoneMenu,ObjectMenu,TypeMenu;
  36. short LastZoneMenuChoice,LastTypeMenuChoice,LastObjectMenuChoice;
  37. SysEnvRec gMac;
  38. short gMenuItem;
  39.  
  40. /*****************************************************************/
  41. /*
  42. /* E X T E R N A L S
  43. /*
  44. /*****************************************************************/
  45.  
  46.  
  47. extern    myFillRoundRectAnimationRtn (Rect r, PatPtr pat);
  48. extern     void connectToPeer();
  49. extern     void removeADSP();
  50. extern     void initializeADSP();
  51. extern     removeMyName();
  52. extern     void ADSPLoop();
  53. extern     OSErr InitAppleTalk();
  54. extern     Boolean ATPZoneRequest (MenuHandle zoneMenu);
  55. extern     LookupNames (MenuHandle lookupMenu,Boolean doObjects);
  56. extern     GetZones();
  57. extern     GetOurZone();
  58. extern    void writeOutgoing(DSPPBPtr dspPBPtr,
  59.                             short ccbRefNum,
  60.                             Ptr dataPtr,
  61.                             short reqCount);
  62. extern     void removeConnectionEnd();
  63. extern    void removeADSPBuffers();
  64.  
  65. extern     Str255 gZoneString,gTypeStr,gObjStr;
  66. extern     Boolean gConnectionWasJustMade;
  67. extern    TRCCB gMyCCB;                /* our ccb */
  68. extern    QHdr    gAvailQueue,gReadQueue,gDoneQueue;
  69. extern    short     gCCBRefNum;            /* ccb reference number returned by adsp on a dspInit */
  70. extern    Ptr outgoingDataBuffer;
  71. extern    DSPPBPtr GetQElement(QHdrPtr qHdrPtr);
  72.  
  73.  
  74. #pragma segment Main
  75.  
  76. // *****************************************************************
  77. // *    CopyPstr
  78. // *
  79. // *    copies a pascal string
  80. // *****************************************************************
  81. void CopyPstr(Ptr pSource, Ptr pDest)
  82. {
  83.     BlockMove(pSource, pDest, pSource[0]+1);
  84. }
  85.  
  86. // *****************************************************************
  87. // *    PStrCat
  88. // *
  89. // *    appends pascal string sourceStr, to pascal string destinationStr.
  90. // *    Overflow is checked, and the copy halts if the destination
  91. // *    string is filled.
  92. // *****************************************************************
  93. void PStrCat(Ptr sourceStr, Ptr destinationStr)
  94. {
  95.     unsigned int    srcIndex, dstIndex;
  96.     unsigned int    bytesToCopy;
  97.     
  98.     srcIndex = 1;
  99.     dstIndex = destinationStr[0] + 1;
  100.     bytesToCopy = sourceStr[0];
  101.     
  102.     while(bytesToCopy > 0 && dstIndex < 255)
  103.     {
  104.         destinationStr[dstIndex] = sourceStr[srcIndex];
  105.         dstIndex++;
  106.         srcIndex++;
  107.         bytesToCopy--;
  108.     }
  109.     destinationStr[0] = dstIndex - 1;
  110. }
  111.  
  112. // *****************************************************************
  113. // *    DoActivate
  114. // *
  115. // * This is called when a window is activated or deactivated.
  116. // * In Sample, the Window Manager's handling of activate and
  117. // * deactivate events is sufficient. Other applications may have
  118. // * TextEdit records, controls, lists, etc., to activate/deactivate.
  119. // *****************************************************************
  120. void DoActivate (WindowPtr window, Boolean becomingActive)
  121. {
  122. #pragma unused (window,becomingActive)
  123.  
  124. } /*DoActivate*/
  125.  
  126.  
  127. // *****************************************************************
  128. // *    ShowError
  129. // *
  130. // * Our routine for displaying an error message in a dialog box.
  131. // *****************************************************************
  132. void ShowError(short index)
  133. {
  134. short itemHit;
  135.  
  136.  
  137.     switch (index)
  138.     {
  139.         case atalkErr:
  140.             ParamText("\perror loading AppleTalk drivers!","\p","\p","\p");
  141.             break;
  142.         case memErr:
  143.             ParamText("\pmemory error.","\p","\p","\p");
  144.             break;
  145.         case menuErr:
  146.             ParamText("\perror initializing menus.","\p","\p","\p");
  147.             break;
  148.         case nbpErr:
  149.             ParamText("\pCouldn't register our name on the network (duplicate already exists).","\p","\p","\p");
  150.             break;
  151.         case noTargetErr:
  152.             ParamText("\pTarget not found.","\p","\p","\p");
  153.             break;
  154.         case noConnErr:
  155.             ParamText("\pNo connection has been made.","\p","\p","\p");
  156.             break;
  157.         case writeNotDoneErr:
  158.             ParamText("\pPrevious write has not completed yet.","\p","\p","\p");
  159.             break;
  160.         case badROMsErr:
  161.             ParamText("\pYour machine does not have at least 128K ROMs.","\p","\p","\p");
  162.             break;
  163.         case heapErr:
  164.             ParamText("\pNot enough heap space.","\p","\p","\p");
  165.             break;
  166.         case noMemErr:
  167.             ParamText("\pNot enough memory available for our application.","\p","\p","\p");
  168.             break;
  169.         case DrvrErr:
  170.             ParamText("\pFatal Device Manager error.","\p","\p","\p");
  171.             break;
  172.         case ListenErr:
  173.             ParamText("\pCould not setup connection listener.","\p","\p","\p");
  174.             break;
  175.         case dspInitErr:
  176.             ParamText("\pADSP Initialization failed.","\p","\p","\p");
  177.             break;
  178.         case dspOpenErr:
  179.             ParamText("\pAttempt to open connection failed.","\p","\p","\p");
  180.             break;
  181.         case dspRemoveErr:
  182.             ParamText("\pError returned while eliminating the connection end.","\p","\p","\p");
  183.             break;
  184.         
  185.         default:
  186.             ParamText("\pdefault error displayed here.","\p","\p","\p");
  187.             break;
  188.  
  189.     }
  190.     
  191.     itemHit = Alert(rErrorDialog, nil);
  192.  
  193. }
  194.  
  195. // *****************************************************************
  196. // *    FatalError
  197. // *
  198. // * This is called when we detect that we cannot operate in the
  199. // * current environment.
  200. // *****************************************************************
  201. void FatalError(error)
  202.     short error;
  203. {
  204.  
  205.     ShowError(error);
  206.     ExitToShell();
  207. }
  208.  
  209. // *****************************************************************
  210. // *    IsAppWindow
  211. // *
  212. // *    Tells us whether or not a window is an application window
  213. // *
  214. // *****************************************************************
  215. Boolean IsAppWindow(window)
  216.     WindowPtr    window;
  217. {
  218.     short        windowKind;
  219.  
  220.     if ( window == nil )
  221.         return false;
  222.     else {    /* application windows have windowKinds >= userKind (8) or dialogKind (2) */
  223.         windowKind = ((WindowPeek) window)->windowKind;
  224.         return (windowKind >= userKind) || (windowKind == dialogKind);
  225.     }
  226. } /* IsAppWindow */
  227.  
  228. // *****************************************************************
  229. // *    DialogEditing
  230. // *
  231. // *    handles the standard editing operations (cut, copy, paste)
  232. // *    for our dialog.
  233. // *****************************************************************
  234. void DialogEditing (short menuItem)
  235. {
  236.     /* handles Edit menu */
  237.  
  238.     DialogPeek dp;
  239.     short ignore;
  240.     long evenBiggerIgnore;
  241.  
  242.         dp = (DialogPeek)myDialog;
  243.  
  244.         switch(menuItem)
  245.         {
  246.             case iCut: 
  247.                     evenBiggerIgnore = ZeroScrap(); /* you really shouldn't ignore this error.  see TESample for details */
  248.                     TECut(dp->textH);
  249.                     ignore = TEToScrap; /* you really shouldn't ignore this error.  see TESample for details */
  250.                 break;
  251.             case iCopy: 
  252.                     evenBiggerIgnore = ZeroScrap(); /* you really shouldn't ignore this error.  see TESample for details */
  253.                     TECopy(dp->textH);
  254.                     ignore = TEToScrap; /* you really shouldn't ignore this error.  see TESample for details */
  255.                 break;
  256.             case iPaste: 
  257.                         /* you really shouldn't ignore this either.                                               */
  258.                         /* one should be checking the length of the incoming scrap to see if it            */
  259.                         /* will fit in our remaining text edit space.  but since we have a weenine 255    */
  260.                         /* char dialog text edit item in any case we'll blow it off...see TESample for     */
  261.                         /* details on "doing the right thing"                                                    */
  262.                     ignore = TEFromScrap;
  263.                     TEPaste(dp->textH);
  264.                 break;
  265.             case iClear: 
  266.                 TEDelete(dp->textH);
  267.         }
  268. }
  269.  
  270.  
  271. // *****************************************************************
  272. // *    IsDAWindow
  273. // *
  274. // *    Check if a window belongs to a desk accessory.
  275. // *****************************************************************
  276. Boolean IsDAWindow(WindowPtr window)
  277. {
  278.  
  279.     if (window == nil)
  280.         return false;
  281.     else    /* DA windows have negative windowKinds */
  282.         return((((WindowPeek)window)->windowKind < 0) ? true : false);
  283. } /*IsDAWindow*/
  284.  
  285.  
  286. // *****************************************************************
  287. // *    DoCloseWindow
  288. // *
  289. // * Close a window. This handles only desk accessory windows because we do not
  290. // * allow our window to be closed. TESample provides an example of how to handle
  291. // * the closing of application windows.
  292. // *****************************************************************
  293. void DoCloseWindow (WindowPtr window)
  294. {
  295. /* Close a window. This handles only desk accessory windows because we do not*/
  296. /* allow our window to be closed. TESample provides an example of how to handle*/
  297. /* the closing of application windows.*/
  298.  
  299.  
  300.     if (IsDAWindow(window))
  301.         CloseDeskAcc(((WindowPeek)window)->windowKind);
  302. } /*DoCloseWindow*/
  303.  
  304.  
  305. // *****************************************************************
  306. // *    HiliteConnectButton
  307. // *
  308. // *    sets our connect button to the desired state (active or inactive)
  309. // *****************************************************************
  310. void HiliteConnectButton (short mode)
  311. {
  312.     Rect r;
  313.     short kind;
  314.     Handle h;
  315.  
  316.         GetDItem(myDialog, kConnectButtonID, &kind, &h, &r);
  317.         HiliteControl((ControlHandle)h, mode);
  318. }
  319.  
  320. // *****************************************************************
  321. // *    outlinePopUpMenus
  322. // *
  323. // *     this is a group routine for drawing the popup menu outlines
  324. // *     compleat with drop shadow. the menu title is draw to the left
  325. // *    of the popUp item itself by this routine.
  326. // *****************************************************************
  327. void outlinePopUpMenus (WindowPtr whichWindow, Rect r, Str255 itemString)
  328. {
  329.  
  330.  
  331.     FrameRect(&r);
  332.     MoveTo(r.left + 2, r.bottom);
  333.     LineTo(r.right, r.bottom);
  334.     MoveTo(r.right, r.bottom);
  335.     LineTo(r.right, r.top + 2);
  336.     InsetRect(&r, 1, 1);
  337.     EraseRect(&r);
  338.     InsetRect(&r, -1, -1);
  339.     MoveTo(r.left + 5, r.top + 11);
  340.     TextFont(geneva);
  341.     TextSize(9);
  342.     DrawString(itemString);
  343.     drawPopUpTri(whichWindow, r);
  344.  
  345. }
  346.  
  347.  
  348.  
  349.  
  350. // *****************************************************************
  351. // *    UpdateUserItems
  352. // *
  353. // *     update procedure for the user items in our dialog.
  354. // *****************************************************************
  355. pascal void UpdateUserItems (WindowPtr whichWindow,
  356.                              short theItem)
  357. {
  358.     GrafPtr savedPort;
  359.     Rect r;
  360.     short kind;
  361.     Handle h;
  362.  
  363.         GetPort(&savedPort);
  364.         SetPort(whichWindow);
  365.         GetDItem(whichWindow, theItem, &kind, &h, &r);
  366.         switch(theItem)
  367.         {
  368.             case kzoneItemID: 
  369.                 outlinePopUpMenus(whichWindow, r, gZoneString);
  370.                 break;
  371.                 
  372.             case ktypeItemID: 
  373.                 outlinePopUpMenus(whichWindow, r, gTypeStr);
  374.                 break;
  375.  
  376.             case kobjectItemID: 
  377.                 outlinePopUpMenus(whichWindow, r, gObjStr);
  378.                 break;
  379.  
  380.             case kRemoteMacsTimeBorderID: 
  381.                 UpdateItemBorder(theItem, r);
  382.                 break;
  383.  
  384.             case kIncomingMessageBorderID: 
  385.                 UpdateItemBorder(theItem, r);
  386.                 break;
  387.  
  388.             case kConnectStatusBorder: 
  389.                 UpdateItemBorder(theItem, r);
  390.                 break;
  391.  
  392.             case kPopupBorderID: 
  393.                 UpdateItemBorder(theItem, r);
  394.                 break;
  395.  
  396.         }
  397.         
  398.         SetPort(savedPort);
  399. }
  400.  
  401.  
  402. // *****************************************************************
  403. // *    DoModeless
  404. // *
  405. // * this handles mouse clicks inside our main dialog
  406. // *****************************************************************
  407. void DoModeless (DialogPtr whichDialog, short whichItem)
  408. {
  409.     Rect r;
  410.     short kind;
  411.     Handle h;
  412.     MenuHandle allPurposeMenu;
  413.     long menuResult;
  414.     Point pt;
  415.  
  416.  
  417.         if (whichDialog == myDialog)
  418.             {
  419.                 GetDItem(whichDialog, whichItem, &kind, &h, &r);
  420.                 switch(whichItem)
  421.                 {
  422.                     case kQuitButtonID: 
  423.                         Terminate();
  424.                             break;
  425.                     case kzoneItemID:
  426.                         pt.v = r.top;
  427.                         pt.h = r.left + 1;
  428.                         LocalToGlobal(&pt);
  429.                         SetCursor(*(GetCursor(watchCursor)));
  430.                         allPurposeMenu = NewMenu(ZoneMenuID, "");
  431.                         InsertMenu(allPurposeMenu, -1);
  432.                         GetZones(allPurposeMenu);
  433.  
  434.                         InitCursor();
  435.                         menuResult = PopUpMenuSelect(allPurposeMenu, pt.v, pt.h, 1);
  436.                         if (menuResult != 0)
  437.                         {
  438.                             GetItem(allPurposeMenu, LoWord(menuResult), gZoneString);
  439.                             gMenuItem = LoWord(menuResult);
  440.                             CopyPstr("\p", gObjStr);
  441.                             InvalRect(&r);                                        
  442.                         }
  443.                         InitCursor();
  444.                         DeleteMenu(ZoneMenuID);
  445.                         DisposeMenu(allPurposeMenu);
  446.                             break;
  447.                     case ktypeItemID:
  448.                         if (gMoofEntityFilter == false)
  449.                         {
  450.                             pt.v = r.top - 2;
  451.                             pt.h = r.left + 1;
  452.                             LocalToGlobal(&pt);
  453.                             allPurposeMenu = NewMenu(TypeMenuID, "");
  454.                             InsertMenu(allPurposeMenu, -1);
  455.                             SetCursor(*(GetCursor(watchCursor)));
  456.                             LookupNames(allPurposeMenu, false);
  457.                             InitCursor();
  458.                             menuResult = PopUpMenuSelect(allPurposeMenu, pt.v, pt.h, 1);
  459.                             if (menuResult != 0)
  460.                             {
  461.                                 GetItem(allPurposeMenu, LoWord(menuResult), gTypeStr);
  462.                                 if (strcmp(gTypeStr,"Moof") == 0)
  463.                                     HiliteConnectButton(0);
  464.                                 else
  465.                                     HiliteConnectButton(255);
  466.                                 gMenuItem = LoWord(menuResult);
  467.                                 InvalRect(&r);
  468.                             }
  469.                             
  470.                             DeleteMenu(TypeMenuID);
  471.                             DisposeMenu(allPurposeMenu);
  472.                         }
  473.                         else
  474.                             SysBeep(1);
  475.                             break;
  476.                     case kobjectItemID:
  477.                         pt.v = r.top - 2;
  478.                         pt.h = r.left + 1;
  479.                         LocalToGlobal(&pt);
  480.                         allPurposeMenu = NewMenu(ObjectMenuID, "");
  481.                         InsertMenu(allPurposeMenu, -1);
  482.                         SetCursor(*(GetCursor(watchCursor)));
  483.                         LookupNames(allPurposeMenu, true);
  484.                         InitCursor();
  485.                         menuResult = PopUpMenuSelect(allPurposeMenu, pt.v, pt.h, 1);
  486.                         if (menuResult != 0)
  487.                         {
  488.                             GetItem(allPurposeMenu, LoWord(menuResult), gObjStr);
  489.                             gMenuItem = LoWord(menuResult);
  490.                             InvalRect(&r);
  491.                         }
  492.  
  493.  
  494.                         DeleteMenu(ObjectMenuID);
  495.                         DisposeMenu(allPurposeMenu);
  496.                             break;
  497.                     case kConnectButtonID: /* connect button */
  498.                             connectToPeer();
  499.                             break;
  500.                     case kMoofFilterCheckBox: 
  501.                         if (gMoofEntityFilter == false)
  502.                         {
  503.                             SetCtlValue((ControlHandle)h,1);
  504.                             gMoofEntityFilter = true;
  505.                             CopyPstr("\pMoof", gTypeStr);
  506.                             HiliteConnectButton(0);
  507.                         }
  508.                         else
  509.                         {
  510.                             SetCtlValue((ControlHandle)h,0);
  511.                             gMoofEntityFilter = false;
  512.                             CopyPstr("\p", gTypeStr);
  513.                             if (gMyCCB.state == sClosed)
  514.                                 HiliteConnectButton(255);
  515.                             
  516.                         }
  517.                         
  518.                         gObjStr[0] = 0;
  519.                         InvalRect(&myDialog->portRect);
  520.  
  521.                             break;    /* iMoofFilter */
  522.  
  523.                         
  524.                 } /* of case */
  525.             }
  526.     }
  527.  
  528. // *****************************************************************
  529. // *    setEachUserItem
  530. // *
  531. // *    sets the update routine for our dialogs user items
  532. // *****************************************************************
  533. void setEachUserItem (short item)
  534. {
  535.     Rect r;
  536.     short kind;
  537.     Handle h;
  538.     
  539.         GetDItem(myDialog, item, &kind, &h, &r);
  540.         SetDItem(myDialog, item, userItem, (Handle)UpdateUserItems, &r);
  541. }
  542.  
  543.  
  544. // *****************************************************************
  545. // *    DoMenuCommand
  546. // *
  547. // *This is called when an item is chosen from the menu bar (after calling
  548. // * MenuSelect or MenuKey). It performs the right operation for each command.
  549. // * It is good to have both the result of MenuSelect and MenuKey go to
  550. // * one routine like this to keep everything organized.
  551. // *****************************************************************
  552. void DoMenuCommand (long menuResult)
  553. {
  554.     short menuID;        /*the resource ID of the selected menu*/
  555.     short menuItem;        /*the item number of the selected menu*/
  556.     short itemHit;
  557.     Str255 daName;
  558.     short daRefNum;
  559.     Boolean handledByDA;
  560.  
  561.  
  562.         menuID = HiWord(menuResult);    /* use built-ins (for efficiency)...*/
  563.         menuItem = LoWord(menuResult);    /*to get menu item number and menu number*/
  564.         switch (menuID)
  565.         {
  566.         case mApple: 
  567.                 switch(menuItem)
  568.                 {
  569.                     case iAbout:                /*bring up alert for About*/
  570.                         itemHit = Alert(rAboutDialog, nil);
  571.                         break;
  572.                     default:
  573.                             /*all non-About items in this menu are DAs*/
  574.                         GetItem(GetMHandle(mApple), menuItem, daName);
  575.                         daRefNum = OpenDeskAcc(daName);
  576.                         break;
  577.                 }
  578.             break;
  579.         case mFile: 
  580.                 switch(menuItem)
  581.                 {
  582.                     case iClose: 
  583.                         DoCloseWindow(FrontWindow());
  584.                         break;
  585.                     case iQuit: 
  586.                         Terminate();
  587.                         break;
  588.                 }
  589.             break;
  590.         case mEdit:                        /*call SystemEdit for DA editing & MultiFinder*/
  591.                 {
  592.                     if (IsDAWindow(FrontWindow()))
  593.                         handledByDA = SystemEdit(menuItem - 1);
  594.                     else
  595.                         DialogEditing(menuItem);
  596.                 }
  597.             break;
  598.         }
  599.         
  600.         HiliteMenu(0);                    /*unhighlight what MenuSelect (or MenuKey) hilited*/
  601.         
  602. } /*DoMenuCommand*/
  603.  
  604.  
  605. // *****************************************************************
  606. // *    AdjustMenus
  607. // *
  608. // * Enable and disable menus based on the current state.
  609. // * The user can only select enabled menu items. We set up all the menu items
  610. // * before calling MenuSelect or MenuKey, since these are the only times that
  611. // * a menu item can be selected. Note that MenuSelect is also the only time
  612. // * the user will see menu items. This approach to deciding what enable
  613. // * disable state a menu item has the advantage of concentrating all the decision-
  614. // * making in one routine, as opposed to being spread throughout the application.
  615. // * Other application designs may take a different approach that is just as valid.
  616. // *****************************************************************
  617. void AdjustMenus()
  618. {
  619.     WindowPtr window;
  620.     MenuHandle menu;
  621.  
  622.  
  623.         window = FrontWindow();
  624.  
  625.         menu = GetMHandle(mFile);
  626.         if (IsDAWindow(window))                /*we can allow desk accessories to be closed from the menu*/
  627.             EnableItem(menu, iClose);
  628.         else
  629.             DisableItem(menu, iClose);            /*but not our traffic light window*/
  630.  
  631.         menu = GetMHandle(mEdit);
  632.         if (IsDAWindow(window))
  633.         {                            /*a desk accessory might need the undo item */
  634.             EnableItem(menu, iUndo);
  635.             EnableItem(menu, iCut);
  636.             EnableItem(menu, iCopy);
  637.             EnableItem(menu, iPaste);
  638.             EnableItem(menu, iClear);
  639.         }
  640.         else
  641.         {                            /* but we do not support undo!!! */
  642.             DisableItem(menu, iUndo);
  643.             EnableItem(menu, iCut);
  644.             EnableItem(menu, iCopy);
  645.             EnableItem(menu, iPaste);
  646.             EnableItem(menu, iClear);
  647.         }
  648.  
  649. } /*AdjustMenus*/
  650.  
  651. // *****************************************************************
  652. // *    DisplayCurrentStatus
  653. // *
  654. // *    updates our connection status string.
  655. // *****************************************************************
  656. void DisplayCurrentStatus(Ptr displayStr)
  657. {
  658.     Rect r;
  659.     short kind;
  660.     Handle h;
  661.     
  662.     Str255 current;
  663.  
  664.         GetDItem(myDialog, kConnectionStatusString, &kind, &h, &r);
  665.         GetIText(h,¤t);
  666.         if (IUCompString(current,displayStr) != 0)
  667.             SetIText(h, displayStr);
  668.  
  669. }
  670.  
  671.  
  672.  
  673. // *****************************************************************
  674. // *    DisposeQueueMemory
  675. // *
  676. // *    deallocate the memory we used for our error queue
  677. // *****************************************************************
  678. void DisposeQueueMemory(QHdrPtr qHdrPtr)
  679. {
  680.     QElemPtr QElem;
  681.     OSErr err;
  682.  
  683.             /* if free queue has any items, dispose of them */
  684.         while (qHdrPtr->qHead != nil)
  685.         {
  686.             QElem = qHdrPtr->qHead;
  687.             err = Dequeue(QElem,qHdrPtr);
  688.             DisposePtr((Ptr)QElem);
  689.         }
  690. }
  691.  
  692.  
  693. // *****************************************************************
  694. // *    Terminate
  695. // *
  696. // *    if we need to quit the program, we remove the connection
  697. // *    end (if any), deallocate any memory we used, remove our
  698. // *    nbp name from the network and exit.
  699. // *****************************************************************
  700. void Terminate()
  701. {
  702.         /* did we establish a connection end? */
  703.     if (gCCBRefNum != 0)
  704.             /* do a dspRemove to close the connection end */
  705.         removeConnectionEnd(gCCBRefNum);
  706.  
  707.         /* deallocate the memory we used for our adsp buffers */
  708.     removeADSPBuffers();
  709.         /* remove our nbp name "moof" */
  710.     removeMyName();
  711.  
  712.         /* dispose of memory we allocated
  713.             for our queues */
  714.     DisposeQueueMemory(&gAvailQueue);
  715.     DisposeQueueMemory(&gDoneQueue);
  716.     DisposeQueueMemory(&gReadQueue);
  717.     
  718.     ExitToShell();
  719. }
  720.  
  721. // *****************************************************************
  722. // *    Exit
  723. // *
  724. // *    this routine is called for fatal control call errors. We exit
  725. // *    the program in this situation.
  726. // *****************************************************************
  727. void Exit(short message)
  728. {
  729.     ShowError(message);
  730.     Terminate();
  731. }
  732.  
  733. // *****************************************************************
  734. // *    DoIdleProc
  735. // *
  736. // *    this routine is called each time through our event loop. We
  737. // *    process any adsp errors that were returned and check for connections
  738. // *    coming and going.
  739. // *****************************************************************
  740. void DoIdleProc()
  741. {
  742.     ADSPLoop();
  743. }
  744.  
  745. // *****************************************************************
  746. // *    AdjustCursor
  747. // *
  748. // *Change the cursor's shape, depending on its position. This also calculates the region
  749. // * where the current cursor resides (for WaitNextEvent). If the mouse is ever outside of
  750. // * that region, an event would be generated, causing this routine to be called,
  751. // * allowing us to change the region to the region the mouse is currently in. If
  752. // * there is more to the event than just “the mouse moved”, we get called before the
  753. // * event is processed to make sure the cursor is the right one. In any (ahem) event,
  754. // * this is called again before we fall back into WNE.
  755. // *****************************************************************
  756. void AdjustCursor (Point mouse, RgnHandle region)
  757. {
  758.     WindowPtr window;
  759.     RgnHandle arrowRgn;
  760.     RgnHandle ourRgn;
  761.     Rect globalPortRect;
  762.  
  763.     Rect r;
  764.     short kind;
  765.     Handle h;
  766.  
  767.         window = FrontWindow();    /*we only adjust the cursor when we are in front*/
  768.         if ((!gInBackground) && (!IsDAWindow(window)))
  769.         {
  770.                 /*calculate regions for different cursor shapes*/
  771.             arrowRgn = NewRgn();
  772.             ourRgn = NewRgn();
  773.  
  774.                 /*start with a big, big rectangular region*/
  775.             SetRectRgn(arrowRgn, extremeNeg, extremeNeg, extremePos, extremePos);
  776.  
  777.                 /*calculate ourRgn*/
  778.             if (IsAppWindow(window))
  779.             {
  780.                 SetPort(window);            /*make a global version of the portRect*/
  781.                 SetOrigin(-window->portBits.bounds.left, -window->portBits.bounds.top);
  782.                 globalPortRect = window->portRect;
  783.                 RectRgn(ourRgn, &globalPortRect);
  784.                 SectRgn(ourRgn, window->visRgn, ourRgn);
  785.                 SetOrigin(0, 0);
  786.             }
  787.  
  788.                 /*subtract other regions from arrowRgn*/
  789.             DiffRgn(arrowRgn, ourRgn, arrowRgn);
  790.  
  791.                 /*change the cursor and the region parameter*/
  792.             if (PtInRgn(mouse, ourRgn))
  793.             {
  794.                 GetDItem(myDialog, kOutgoingTextID, &kind, &h, &r);
  795.                 GlobalToLocal(&mouse);
  796.                 if (PtInRect(mouse, &r))
  797.                     SetCursor(*GetCursor(iBeamCursor));
  798.                 else
  799.                     SetCursor(&qd.arrow);
  800.                 CopyRgn(ourRgn, region);
  801.             }
  802.             else
  803.             {
  804.  
  805.                 SetCursor(&qd.arrow);
  806.                 CopyRgn(arrowRgn, region);
  807.  
  808.             }
  809.  
  810.                 /*get rid of our local regions*/
  811.             DisposeRgn(arrowRgn);
  812.             DisposeRgn(ourRgn);
  813.         }
  814. } /*AdjustCursor*/
  815.  
  816. // *****************************************************************
  817. // *    UpdateItemBorder
  818. // *
  819. // *    re-draws the borders around our items
  820. // *****************************************************************
  821. void UpdateItemBorder (short item, Rect r)
  822. {
  823.     FontInfo fInfo;
  824.     Rect tRect;
  825.     Str255 theBorderString;
  826.  
  827.         switch(item)
  828.         {
  829.             case kRemoteMacsTimeBorderID:
  830.                     strcpy(&theBorderString,"Time from remote Mac");
  831.                 break;
  832.             case kIncomingMessageBorderID:
  833.                     strcpy(&theBorderString,"Incoming message");
  834.                 break;
  835.             case kPopupBorderID:
  836.                     strcpy(&theBorderString,"Select A Target Machine");
  837.                 break;
  838.             case kOutgoingMessageBorderID:
  839.                     strcpy(&theBorderString,"Outgoing message");
  840.                 break;
  841.             case kConnectStatusBorder:
  842.                     strcpy(&theBorderString,"Connection Status");
  843.                 break;
  844.  
  845.             default:
  846.                 break;
  847.         }
  848.  
  849.         c2pstr(theBorderString);
  850.  
  851.         GetFontInfo(&fInfo);
  852.         FrameRect(&r);
  853.         MoveTo(r.left + 5, r.top + fInfo.ascent / 2 - 1);
  854.         tRect.left = r.left + 4;
  855.         tRect.right = tRect.left + StringWidth(theBorderString) + 1;
  856.         tRect.top = r.top;
  857.         tRect.bottom = r.top + 1;
  858.         EraseRect(&tRect);
  859.         DrawString(&theBorderString);
  860.  
  861. }
  862.  
  863. // *****************************************************************
  864. // *    PlotSICN
  865. // *
  866. // * this is the PlotSICN code stolen from Tech Note #
  867. // *****************************************************************
  868. void PlotSICN (Rect theRect,SICNHand theSICN, short theIndex)
  869. {
  870.  
  871.     SignedByte state;    /* we want a chance to restore original state */
  872.     BitMap srcBits;        /* built up around 'SICN' data so we can _CopyBits */
  873.  
  874.         /* check the index for a valid value */
  875.         if ((GetHandleSize((Handle)theSICN) / sizeof(SICN)) > theIndex)
  876.         {
  877.  
  878.             /* store the resource's current locked/unlocked condition */
  879.             state = HGetState((Handle)theSICN);
  880.  
  881.             /* lock the resource so it won't move during the _CopyBits call */
  882.             HLock((Handle)theSICN);
  883.  
  884.             /* set up the small icon's bitmap */
  885.             /*$PUSH*/
  886.             /*$R-*/
  887.             /* turn off range checking */
  888.             srcBits.baseAddr = (Ptr)(&(**theSICN[theIndex]));
  889.             /*$POP*/
  890.             srcBits.rowBytes = 2;
  891.             SetRect(&srcBits.bounds, 0, 0, 16, 16);
  892.  
  893.             /* draw the small icon in the current grafport */
  894.             CopyBits(&srcBits, &qd.thePort->portBits, &srcBits.bounds, &theRect, srcCopy, nil);
  895.  
  896.             /* restore the resource's locked/unlocked condition */
  897.             HSetState((Handle)theSICN, state);
  898.         }
  899.  
  900. }
  901.  
  902. // *****************************************************************
  903. // *    drawPopUpTri
  904. // *
  905. // * this procedure draws the new "standard" SICN triangle now used
  906. // * in popup menus I guess the drop shadow was not enough of an
  907. // * indiciation to the users of the presence of the pop-up.
  908. // * standard in 7.0 and up systems 
  909. // *****************************************************************
  910. void drawPopUpTri (WindowPtr whichWindow, Rect r)
  911. {
  912.  
  913. #pragma unused (whichWindow)
  914.  
  915.     Handle popUpTri;
  916.     Rect popUpTriRect;
  917.  
  918.         popUpTri = GetResource('SICN', kStandardTriSICN);
  919.         if (popUpTri)
  920.         {
  921.             popUpTriRect = r;
  922.             popUpTriRect.right = popUpTriRect.right - 1;
  923.             popUpTriRect.left = popUpTriRect.right - 16;
  924.             popUpTriRect.top = popUpTriRect.top + 1;
  925.             popUpTriRect.bottom = popUpTriRect.top + 16;
  926.             PlotSICN(popUpTriRect, (SICNHand)popUpTri, 0);
  927.             ReleaseResource(popUpTri);
  928.         }
  929.  
  930.  
  931. }
  932.  
  933. // *****************************************************************
  934. // *    DoEvent
  935. // *
  936. // * Do the right thing for an event. Determine what kind of event it is, and call
  937. // * the appropriate routines.
  938. // *****************************************************************
  939. void DoEvent (EventRecord event)
  940. {
  941.     short part;
  942.     char key;
  943.     WindowPtr whichWindow;
  944.     DialogPeek dp;
  945.     short teLength;
  946.     DSPPBPtr dspPBPtr;
  947.  
  948.  
  949.     whichWindow = FrontWindow();
  950.  
  951.         switch(event.what)
  952.         {
  953.             case nullEvent: 
  954.                 break;
  955.             case mouseDown: 
  956.                 part = FindWindow(event.where, &whichWindow);
  957.                 if (part != 0)
  958.                 {
  959.                     switch(part)
  960.                     {
  961.                         case inMenuBar: 
  962.                             /*process the menu command*/
  963.                                 AdjustMenus();
  964.                                 DoMenuCommand(MenuSelect(event.where));
  965.                             break;
  966.                         case inSysWindow:                /*let the system handle the mouseDown*/
  967.                             SystemClick(&event, whichWindow);
  968.                             break;
  969.  
  970.                         case inContent: 
  971.                             if (whichWindow != FrontWindow())
  972.                                 SelectWindow(whichWindow);
  973.                             break;
  974.  
  975.                         case inDrag:                        /*pass screenBits.bounds to get all gDevices*/
  976.                             DragWindow(whichWindow, event.where, &qd.screenBits.bounds);
  977.                             break;
  978.                         
  979.                         case inGrow: 
  980.                             break;
  981.                         case inZoomIn:
  982.                         case inZoomOut:
  983.                             break;
  984.                     }
  985.                 }
  986.                 break;
  987.             case keyDown:
  988.             case autoKey:
  989.                                 /*check for menukey equivalents*/
  990.                     key = (char)(event.message & charCodeMask);
  991.                     if ((event.modifiers & cmdKey) != 0)    /*Command key down*/
  992.                     {
  993.                         if ((event.what == keyDown) || (event.what == autoKey))
  994.                         {
  995.                             AdjustMenus();            /*enable/disable/check menu items properly*/
  996.                             DoMenuCommand(MenuKey(key));
  997.                         }
  998.                     }
  999.                     else if (key == (char)kEnterKey) /* enter key */
  1000.                     {
  1001.                         if ( (gMyCCB.state == sClosed) ||
  1002.                                 (gMyCCB.state == sPassive))
  1003.                             ShowError(noConnErr);
  1004.                         else
  1005.                         {
  1006.                             dp = (DialogPeek)myDialog;
  1007.                     
  1008.                                 /* we'll take a maximum of 255 characters from the edit box */
  1009.                             teLength = ((**(*dp).textH).teLength < 255) ? (**(*dp).textH).teLength : 255;
  1010.                     
  1011.                                 /* grab the text from our edit text box */
  1012.                             BlockMove( *((**(*dp).textH).hText), outgoingDataBuffer, teLength);
  1013.                             
  1014.                                 /* we'll make sure we get a parameter block
  1015.                                     so we can send our data */
  1016.                             do
  1017.                             {
  1018.                                     /* get QElement for the write call */
  1019.                                 dspPBPtr = GetQElement(&gAvailQueue);
  1020.                             }
  1021.                             while ( (dspPBPtr == NULL) );
  1022.                             
  1023.                             writeOutgoing(dspPBPtr,
  1024.                                             gCCBRefNum,
  1025.                                             outgoingDataBuffer,
  1026.                                             teLength);
  1027.  
  1028.                             /* Change selection to outgoing text TERecord */
  1029.                             SelIText(myDialog, kOutgoingTextID, 0, 32767);
  1030.                         
  1031.                         }
  1032.                     
  1033.                     }
  1034.                 break;                                /*call DoActivate with the window and...*/
  1035.             case activateEvt:                        /*true for activate, false for deactivate*/
  1036.                 DoActivate((WindowPtr)event.message, (event.modifiers & activeFlag) != 0);
  1037.                 break;
  1038.             case updateEvt: 
  1039.                 break;
  1040.             case osEvent: 
  1041.                 switch(event.message >> 24)    /*high byte of message*/
  1042.                 {
  1043.                     case suspendResumeMessage:
  1044.                             gInBackground = (event.message & resumeMask) == 0;
  1045.                             DoActivate(FrontWindow(), !gInBackground);
  1046.                     break;
  1047.                 }
  1048.  
  1049.         }
  1050.     } /*DoEvent*/
  1051.  
  1052. // *****************************************************************
  1053. // *    EventLoop
  1054. // *
  1055. // * Get events forever, and handle them by calling DoEvent.
  1056. // * Get the events by calling WaitNextEvent, if it's available, otherwise
  1057. // * by calling GetNextEvent. Also call AdjustCursor each time through the loop.
  1058. // *****************************************************************
  1059. void EventLoop()
  1060. {
  1061.     RgnHandle cursorRgn;
  1062.     Boolean gotEvent;
  1063.     EventRecord event;
  1064.     long sleepTime;
  1065.     WindowPtr whichDialog;
  1066.     short whichItem;
  1067.     
  1068.     
  1069.         cursorRgn = NewRgn();            /*we’ll pass WNE an empty region the 1st time thru*/
  1070.         sleepTime = 2;
  1071.         do
  1072.         {
  1073.             if (gHasWaitNextEvent)    /*put us 'asleep' forever under MultiFinder*/
  1074.             {
  1075.                 gotEvent = WaitNextEvent(everyEvent, &event, sleepTime, cursorRgn);
  1076.             }
  1077.             else
  1078.             {
  1079.                 SystemTask();                /*must be called if using GetNextEvent*/
  1080.                 gotEvent = GetNextEvent(everyEvent, &event);
  1081.             }
  1082.  
  1083.             DoIdleProc();
  1084.             AdjustCursor(event.where, cursorRgn); /*make sure we have the right cursor*/
  1085.             whichDialog = FrontWindow();
  1086.             
  1087.                 /* don't pass command keys or <enter> keys to our dialog */
  1088.             if (!((event.what==keyDown)&&(event.modifiers & cmdKey)) 
  1089.             && (IsDialogEvent(&event))
  1090.             && !((event.what==keyDown)&&((event.message & charCodeMask) == (char)kEnterKey)))        
  1091.             {
  1092.                 if (DialogSelect(&event, &whichDialog, &whichItem))
  1093.                     DoModeless(whichDialog, whichItem);
  1094.             }
  1095.             else        
  1096.                 DoEvent(event);
  1097.  
  1098.         }
  1099.         while (true);                    /*loop forever; we quit through an ExitToShell*/
  1100. }
  1101.  
  1102.  
  1103.  
  1104. // *****************************************************************
  1105. // *    TrapAvailable
  1106. // *
  1107. // * Check to see if a given trap is implemented. This is only used by the
  1108. // * Initialize routine in this program, so we put it in the Initialize segment.
  1109. // * The recommended approach to see if a trap is implemented is to see if
  1110. // * the address of the trap routine is the same as the address of the
  1111. // * Unimplemented trap.
  1112. // *****************************************************************
  1113. #pragma segment Initialize
  1114. Boolean TrapAvailable(tNumber,tType)
  1115.     short        tNumber;
  1116.     TrapType    tType;
  1117. {
  1118.     if ( ( tType == ToolTrap ) &&
  1119.         ( gMac.machineType > envMachUnknown ) &&
  1120.         ( gMac.machineType < envMacII ) )
  1121.     {        /* it's a 512KE, Plus, or SE */
  1122.         tNumber = tNumber & 0x03FF;
  1123.         if ( tNumber > 0x01FF )                    /* which means the tool traps */
  1124.             tNumber = _Unimplemented;            /* only go to 0x01FF */
  1125.     }
  1126.     return NGetTrapAddress(tNumber, tType) != GetTrapAddress(_Unimplemented);
  1127. } /*TrapAvailable*/
  1128.  
  1129. // *****************************************************************
  1130. // *    zeroOutStrings
  1131. // *
  1132. // *    clears out the name, time and incoming text strings
  1133. // *****************************************************************
  1134. void zeroOutStrings()
  1135. {
  1136. /* this procedure emptys all connection oriented strings in the main dialog */
  1137.  
  1138.     Rect r;
  1139.     short kind;
  1140.     Handle h;
  1141.  
  1142.         GetDItem(myDialog, kRemoteMacsNameID, &kind, &h, &r);
  1143.         SetIText(h, "\p");
  1144.         GetDItem(myDialog, kRemoteMacsTimeID, &kind, &h, &r);
  1145.         SetIText(h, "\p");
  1146.         GetDItem(myDialog, kIncomingTextID, &kind, &h, &r);
  1147.         SetIText(h, "\p");
  1148. }
  1149.  
  1150. // *****************************************************************
  1151. // *    SetupUserItems
  1152. // *
  1153. // *****************************************************************
  1154. void SetupUserItems()
  1155. {
  1156.     setEachUserItem(kzoneItemID);
  1157.     setEachUserItem(ktypeItemID);
  1158.     setEachUserItem(kobjectItemID);
  1159.     setEachUserItem(kRemoteMacsTimeBorderID);
  1160.     setEachUserItem(kIncomingMessageBorderID);
  1161.     setEachUserItem(kConnectStatusBorder);
  1162.     setEachUserItem(kPopupBorderID);
  1163.  
  1164. /* set connect button to unhilited */
  1165.     HiliteConnectButton(255);
  1166. }
  1167.  
  1168.  
  1169. // *****************************************************************
  1170. // *    Initialize
  1171. // *
  1172. // *    program initialization
  1173. // *****************************************************************
  1174. void Initialize()
  1175. {
  1176.  
  1177.     Rect r;
  1178.     short kind;
  1179.     Handle h;
  1180.     Handle menuBar;
  1181.     DialogPeek dp;
  1182.  
  1183.         gInBackground = false;
  1184.  
  1185.         InitGraf(&qd.thePort);
  1186.         InitFonts();
  1187.         InitWindows();
  1188.         InitMenus();
  1189.         TEInit();
  1190.         InitDialogs(nil);
  1191.         InitCursor();
  1192.  
  1193.  
  1194.         if (InitAppleTalk() != noErr)
  1195.             Exit(atalkErr);
  1196.             
  1197.         gZoneString[0] = 0;gTypeStr[0] = 0;gObjStr[0] = 0;
  1198.  
  1199.         GetOurZone();
  1200.  
  1201.             /*    we will allocate our own window storage instead of letting the Window */
  1202.             /*    Manager do it because GetNewWindow may load in temp. resources before */
  1203.             /*    making the NewPtr call, and this can lead to heap fragmentation. */
  1204.         myDialog = (DialogPtr)(NewPtr(sizeof(DialogRecord)));
  1205.         if (myDialog == nil)
  1206.             Exit(memErr);
  1207.  
  1208.  
  1209.         myDialog = GetNewDialog(rDialog, (Ptr)myDialog, (DialogPtr)-1);
  1210.  
  1211.         SetPort(myDialog);
  1212.  
  1213.         dp = (DialogPeek)myDialog;
  1214.  
  1215.         TextFont(geneva);
  1216.         TextSize(9);
  1217.  
  1218.         (**(dp->textH)).txFont = geneva;
  1219.         (**(dp->textH)).txSize = 9;
  1220.  
  1221.         SetWTitle(myDialog, "\pADSP");
  1222.  
  1223.         ShowWindow(myDialog);
  1224.  
  1225.         LastZoneMenuChoice = 0;
  1226.         LastTypeMenuChoice = 0;
  1227.         LastObjectMenuChoice = 0;
  1228.  
  1229.         SetupUserItems();
  1230.             /* show blinking cursor in outgoing text box */
  1231.         SelIText(myDialog, kOutgoingTextID, 0, 32767);
  1232.  
  1233.             /* turn on "Moof" entity filter (check box) initially */
  1234.         GetDItem(myDialog, kMoofFilterCheckBox, &kind, &h, &r);
  1235.         SetCtlValue((ControlHandle)h,1);
  1236.         gMoofEntityFilter = true;
  1237.         CopyPstr("\pMoof", gTypeStr);
  1238.         gObjStr[0] = 0;
  1239.         HiliteConnectButton(0);
  1240.  
  1241.         
  1242.         menuBar = GetNewMBar(rMenuBar);        /*read menus into menu bar*/
  1243.         if (menuBar == nil)
  1244.             Exit(menuErr);
  1245.         SetMenuBar(menuBar);                    /*install menus*/
  1246.         DisposHandle(menuBar);
  1247.         AddResMenu(GetMHandle(mApple), 'DRVR');    /*add DA names to Apple menu*/
  1248.         DrawMenuBar();
  1249.  
  1250.         gStopped = true;
  1251.         zeroOutStrings();
  1252.         
  1253.         initializeADSP();
  1254.         
  1255. } /*Initialize*/
  1256.  
  1257. // *****************************************************************
  1258. // *    CheckEnvirons
  1259. // *
  1260. // *    check the current operating environment
  1261. // *****************************************************************
  1262. void CheckEnvirons()
  1263. {
  1264.     long  total, contig, response;
  1265.     OSErr ignoreError;
  1266.  
  1267.     
  1268.         /* ignore the error returned from SysEnvirons; even if an error occurred,*/
  1269.         /* the SysEnvirons glue will fill in the SysEnvRec*/
  1270.             ignoreError = SysEnvirons(sysEnvironsVersion, &gMac);
  1271.  
  1272.         /* Make sure that the machine has at least 128K ROMs. If it doesn't, exit. */
  1273.         if (gMac.machineType < 0) FatalError(9);
  1274.         if ((long) GetApplLimit() - (long) ApplicZone() < kMinHeap) FatalError(10);
  1275.         PurgeSpace(&total, &contig);
  1276.         if (total < kMinSpace)
  1277.             if (UnloadScrap() != noErr)
  1278.                 FatalError(11);
  1279.             else
  1280.             {
  1281.                 PurgeSpace(&total, &contig);
  1282.                 if (total < kMinSpace)
  1283.                     FatalError(11);
  1284.             }
  1285.             
  1286.         /* verify if WaitNextEvent, Gestalt and PPCToolbox are available */
  1287.         gHasWaitNextEvent = TrapAvailable(_WaitNextEvent, ToolTrap);
  1288.         if (TrapAvailable(_Gestalt, ToolTrap)) 
  1289.         {         /* verify if system 7.0 */
  1290.             Gestalt(gestaltSystemVersion,&response );
  1291.             if (response >= 0x0700)
  1292.                 gHasSystem7 = true;
  1293.             else gHasSystem7 = false;
  1294.         }
  1295.         else
  1296.         {
  1297.             SysBeep(20);
  1298.             gHasSystem7 = false;
  1299.         }
  1300.  
  1301. }
  1302.  
  1303.  
  1304. // *****************************************************************
  1305. // *    Main
  1306. // *
  1307. // *****************************************************************
  1308. #pragma segment Main
  1309. main()
  1310. {
  1311.     MaxApplZone();            /*expand the heap so code segments load at the top*/
  1312.     CheckEnvirons();        /*check for some basic requirements; exits if not met*/
  1313.     Initialize();            /*initialize the program*/
  1314.     UnloadSeg(&Initialize);    /*note that Initialize must not be in Main!*/
  1315.     EventLoop();            /*call the main event loop*/
  1316.  
  1317. }